 
#include <map>
#include <sstream>
#include <vector>
#include <sys/stat.h>

#include "pqPool.h"
#include "Farm.h"
#include "DFarm.h"
#include "PFarm.h"
#include "MFarm.h"
#include "NFarm.h"
#include "Debug.h"

PqPool * PqPool::_instance = NULL;
unsigned int pgsql_default_port = 5432;
string str_pgsql_default_port = "5432";
#define  DEFAULT_INI_GROUP   "global"

PqPool * PqPool::Instance(const char *confFile)
{
	if (NULL == _instance )
	{
		if(confFile == NULL || strlen(confFile) < 4)
		{
			ERRO << "confFile is invalite!" << endl;
			return NULL;
		}
		_instance = new PqPool();
		try
		{
			if (! _instance->init(confFile) )
			{
				delete _instance;
				_instance = NULL;
			}
		}
		catch(...)
		{ 
			if(_instance != NULL)
				delete _instance;
			_instance = NULL;
		}
	}
	return _instance;
}

int PqPool::GetWriteCounts(int farm_id)
{
   if (pcMap.find(farm_id) == pcMap.end())
	  return 0;
   else
	    return  pcMap[farm_id]->GetWriterSize();
}

PGconn * PqPool::GetWriter(int farm_id, int idx)
{

	if (pcMap.find(farm_id) == pcMap.end())
	{  
		cerr << "farm id = "<< farm_id<<" is not present"<< endl;   
		return NULL;
	}
	else
	{
		PGconn *connxn = pcMap[farm_id]->GetWriter(idx);
		return connxn;
	}
}


PGconn * PqPool::GetReader(int farm_id)
{
   if (pcMap.find(farm_id) == pcMap.end())
   {  
      cerr << "farm id = "<< farm_id<<" is not present"<< endl;   
      return NULL;
   }
   else
   {
        DEBUG2("try to get reader from farm_ID = ", farm_id);
         PGconn *connxn = pcMap[farm_id]->GetReader();
         return connxn;
   }
}


void PqPool::CloseReader(int farm_id,  PGconn *c )
{

   if ( pcMap.find(farm_id) == pcMap.end())
       return;
   else
       pcMap[farm_id]->CloseConnection(c, false);
}

void PqPool::CloseWriter(int farm_id, PGconn *c)
{
	if (pcMap.find(farm_id) == pcMap.end())
	{
		DEBUG3("Farm ",farm_id," not present");
		return ;
	}
	else
	{
		pcMap[farm_id]->CloseConnection(c, true);
	}
}

void PqPool::closeAll()
{
   for (map<int, Farm *>::iterator i = pcMap.begin(); i != pcMap.end(); i++)
   {
	  Farm *p = i->second;
	  if(p != NULL) 
		  delete i->second;
   }
   pcMap.clear();
}


bool PqPool::init(const char * confFile)
{
   pcMap.clear();
   struct stat sbconf;
   if ( stat( confFile, &sbconf) != 0 )
   {
           ERRO << "stat :"<< confFile<<", but error: file not exist ?!" << endl;
	   return false;
   }
   DEBUG3("stat ", confFile , " success!" );
   DbParam dbp;
   GKeyFile *kv = g_key_file_new();
   GError *err = NULL;
   if(TRUE == g_key_file_load_from_file(kv,confFile, G_KEY_FILE_NONE , &err))
   {
	   gchar *pval =  g_key_file_get_value(kv, DEFAULT_INI_GROUP, "dbName",  NULL);
	   if(pval != NULL)
	   {
                  dbp.dbName_.append( g_strstrip(pval));
		  g_free(pval);
	   }
	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "dbUser" ,  NULL);
	   if(pval != NULL)
	   {
		   dbp.dbUser_.append(g_strstrip(pval));
		   g_free(pval);
	   }
	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "dbPass" ,  NULL);
	   if(pval != NULL)
	   {
		   dbp.dbPass_.append(g_strstrip(pval));
		   g_free(pval);
	   }

	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "dbTimeOut" ,  NULL);
	   if(pval != NULL)
	   {
		   dbp.dbTimeOut_ =  atoi(pval);
		   g_free(pval);
	   }
           else
                dbp.dbTimeOut_ = 1;

	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "dbSkipConnections" ,  NULL);
	   if(pval != NULL)
	   {
		   dbp.dbSkipConnections_ = atoi(pval);
		   g_free(pval);
	   }
           else
                  dbp.dbSkipConnections_ = 10;

	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "dbPort" ,  NULL);
	   if(pval != NULL)
	   {
		   dbp.dbPort_ = pval;
		   g_free(pval);
	   }
           else
                  dbp.dbPort_  =  str_pgsql_default_port;
	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "debug" ,  NULL);
	   if(pval != NULL)
	   {
		   atoi((char *)pval) > 0 ? m_bDebug = true : m_bDebug = false;
		   g_free(pval);
	   }

	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "mapMax" ,  NULL);
	   m_iMax = 1; 
	   if(pval != NULL)
	   {
		   m_iMax = atoi(pval);
		   g_free(pval);
	   }
	   m_iMax > 0 ? m_iMax : m_iMax = 0;
          
	   m_MappingIdex = new MappingIndex( m_iMax );
	   pval =  g_key_file_get_value(kv,DEFAULT_INI_GROUP , "farms" ,  NULL);
	   if(pval == NULL)
           {
                   g_key_file_free(kv);
                   cerr << "read farms from [global], but fail" << endl;  
                   return  false;
           }	  
           string sfarms = g_strstrip(pval); 
	   g_free(pval);
	   char **columns = g_strsplit(sfarms.c_str(), ",", -1);
	   int columns_num =  g_strv_length(columns);
	   DEBUG3(sfarms.c_str(), "; sub size = ", columns_num); 
	   for(int i=0; i< columns_num ; i++ )
	   {
		   pval = g_key_file_get_value(kv, g_strstrip(columns[i]), "ID" ,  NULL);
		   if(pval == NULL)
		   {
			   cerr << "read 'ID' from ["<<g_strstrip(columns[i])<<"], but fail!"<<endl; 
			   g_strfreev(columns);
			   g_key_file_free(kv);
			   return false;
		   }
		   int fid = atoi((char *)pval);
		   g_free(pval);
DEBUG2("ID = ", fid);
		   map<int, Farm *>::iterator iter =  pcMap.find(fid);
		   if(iter != pcMap.end())
		   {
			   cerr << "ID = "<< fid << " has been declared!!"<<endl;
			   g_key_file_free(kv);
			   g_strfreev(columns);
			   return false;	
		   }

		   pval = g_key_file_get_value(kv,g_strstrip(columns[i]), "ConnectionType" ,  NULL);
		   if(pval == NULL)
		   {
			   cerr << "read 'ConnectionType' from ["<<g_strstrip(columns[i])<<"], but fail!"<<endl; 
			   g_strfreev(columns);
			   g_key_file_free(kv);
			   return false;
		   }
		   int connType = atoi((char *)pval);
		   g_free(pval);
DEBUG2("ConnectionType = ", connType);           

                   pval =  g_key_file_get_value(kv,g_strstrip(columns[i]), "dbName",  NULL);
                   if(pval != NULL)
                   {
                           dbp.dbName_ =  g_strstrip(pval);
                           g_free(pval);
                   }
                   pval =  g_key_file_get_value(kv, g_strstrip(columns[i]) , "dbUser" ,  NULL);
                   if(pval != NULL)
                   {
                           dbp.dbUser_ = g_strstrip(pval);
                           g_free(pval);
                   }
                   pval =  g_key_file_get_value(kv, g_strstrip(columns[i]), "dbPass" ,  NULL);
                   if(pval != NULL)
                   {
                           dbp.dbPass_ = g_strstrip(pval);
                           g_free(pval);
                   }

		   pval = g_key_file_get_value(kv,g_strstrip(columns[i]), "readers" ,  NULL);
		   if(pval == NULL)
		   {
			   cerr << "read 'readers' from ["<<g_strstrip(columns[i])<<"], but fail!"<<endl; 
			   g_strfreev(columns);
			   g_key_file_free(kv);
			   return false;
		   }
		   string readers = (char *)g_strstrip(pval);
		   g_free(pval);
DEBUG2("readers = ", readers.c_str());
		   pval = g_key_file_get_value(kv, g_strstrip(columns[i]), "writers" ,  NULL);
		   if(pval == NULL)
		   {
			   cerr << "read 'writers' from ["<< g_strstrip(columns[i])<<"], but fail!"<<endl; 
			   g_strfreev(columns);
			   g_key_file_free(kv);
                        return false;
                    }
		   string writers = (char *)g_strstrip(pval);
		   g_free(pval);
DEBUG2("wirters = ", writers.c_str());
		   pval = g_key_file_get_value(kv,g_strstrip(columns[i]), "mapping" ,  NULL);
		   if(pval != NULL)
		   {
			   DEBUG2("mapping = ", (char *)pval);
			   m_MappingIdex->AddLines(pval, fid );		  
			   g_free(pval);
		   }
                   DEBUG1("start to create new Farm !");
		   if (connType == 0) // Demand based Connections
			   pcMap[fid] = new DFarm(dbp, readers.c_str(), writers.c_str(),m_bDebug); 
		   else if (connType == 1)
		   { // Persistance Connections
			   pcMap[fid] = new PFarm(dbp, readers.c_str(), writers.c_str(), m_bDebug); 
		   }
		   else if (connType == 2)
		   { // Persistance writers but Decand readers Connections
			   pcMap[fid] = new MFarm(dbp, readers.c_str(), writers.c_str(), m_bDebug); 
		   }
		   else if (connType == 3)
		   { // Persistance reader but Decand writer Connections
			   pcMap[fid] = new NFarm(dbp, readers.c_str(), writers.c_str(), m_bDebug); 
		   }
		   else
		   {
                           g_strfreev(columns);
			   g_key_file_free(kv);
                           cerr << "ConnectionType ="<< connType <<", it should in [0,2] !"<<endl; 
			   return  false;
		   }
	   }
	   g_strfreev(columns);
	   g_key_file_free(kv);
           DEBUG1("check MappingIndex ...");
	   if(m_MappingIdex == NULL ||  ! m_MappingIdex->Check())
           {
                   cerr << "m_MappingIndex is error !"<<endl;
                   if(m_MappingIdex != NULL)
                       m_MappingIdex->Print(); 
		   return false;
           }
           DEBUG1("Check pcMap");
	   for(int i=0; i< (int)pcMap.size(); i++)
	   {
		   if(pcMap.find(i) == pcMap.end())
		   {
			   cerr << "farm's IDs are not continue-number! "<<endl;
			   return false;
		   }
	   }
	   return true;
   }
   else if (err != NULL)
   {
	   cerr << "Unable to read file:" << err->message << endl;
	   g_error_free (err);
   }
   g_key_file_free(kv);
   return false;
}

void PqPool:: Print()
{
	m_MappingIdex->Print();
	for (map<int, Farm *>::iterator iter = pcMap.begin(); iter != pcMap.end(); iter++)
	{
		Farm *p = iter->second;
		if(p != NULL)
                {
                    cout<<"Farm ID="<< iter->first<<endl;  
                    p->Print();
                }
	}
}

int PqPool:: GetIDbyKey(int key)
{
     return m_MappingIdex->GetKeybyNumber(key);
}

PqPool::~PqPool() 
{
	_instance->closeAll();
	if(m_MappingIdex != NULL)
	{
		delete m_MappingIdex;
		m_MappingIdex = NULL;
	}
}

void PqPool::Release()
{
	if (_instance )
	{
		delete _instance;
		_instance = NULL;
	}
}

